package jdk.jdk8.stream;

import jdk.jdk8.optional.User;
import org.junit.Test;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 3、终止操作 + 规约
 * 短路（Short-circuiting）操作：指遇到某些符合条件的元素就可以得到最终结果（anyMatch、allMatch、findFirst、findAny、noneMatch）；
 * 非短路（Unshort-circuiting）操作：指必须处理完所有元素才能得到最终结果（foreach、forEachOrdered、toArray、reduce、collect、max、min、count）。
 *
 * @author guod
 * @version 1.0
 * @since 1.8
 */
public class StreamMain03 {
    // 归约reduce
    @Test
    public void test() {
        // T reduce(T identity, BinaryOperator<T> accumulator);
        List<Integer> integers = Arrays.asList(1, 3, 4, 6);
        System.out.println(integers.stream().reduce(0, Integer::sum));
        // Optional<T> reduce(BinaryOperator<T> accumulator);
        System.out.println(StreamMain02.createUser().stream().map(User::getAge).reduce(Integer::sum));
    }

    // 收集
    @Test
    public void collect() {
        List<User> userList = StreamMain02.createUser().stream().filter(e -> e.getAge() > 8)
                .collect(Collectors.toList());
        userList.forEach(System.out::println);

        Set<User> userSet = StreamMain02.createUser().stream().filter(e -> e.getAge() > 8)
                .collect(Collectors.toSet());
        userSet.forEach(System.out::println);

    }

    //// Collectors.toMap 有三个重载方法
    // == Map中Key所选择对象中的字段去重策略,有多个相同的key,保留第一个
    @Test
    public void list2Map01() {
        Map<Integer, String> map01 = StreamMain02.createUser().stream()
                .collect(Collectors.toMap(User::getAge, User::getName));
        // 如果希望得到Map的value为对象本身时，可以下面两种写法
        Map<String, User> map02 = StreamMain02.createUser().stream()
                .collect(Collectors.toMap(User::getName, t -> t));
        Map<String, User> map03 = StreamMain02.createUser().stream().
                collect(Collectors.toMap(User::getName, Function.identity()));
    }

    @Test
    public void list2Map02() {
        List<User> users = StreamMain02.createUser();
        users.add(new User("user1", 12));
        users.add(new User("user1", 55));
        // 如果存在相同的key取k1
        Map<String, User> map1 = users.stream()
                // 键-key & 值-value & 传入合并函数，Key冲突 & 希望返回的Map
                .collect(Collectors.toMap(User::getName, m -> m, (k1, k2) -> k1));
        System.out.println(map1);
        System.out.println();

        // 返回一个排序后的Map
        TreeMap<String, User> collect = StreamMain02.createUser().stream()
                .collect(Collectors.toMap(User::getName, v -> v, (o1, o2) -> o2, TreeMap::new));
        System.out.println(collect);
    }

    // 分组 & 分区
    @Test
    public void group() {
        // 分组
        Map<String, List<User>> group1 = StreamMain02.createUser().stream().collect(Collectors.groupingBy(User::getName));
        System.out.println((long) group1.entrySet().size());
        Map<String, Map<Integer, List<User>>> group2 = StreamMain02.createUser().stream()
                .collect(Collectors.groupingBy(User::getName, Collectors.groupingBy(User::getAge)));
        // 分区，分成两部分，一部分大于10岁，一部分小于等于10岁
        Map<Boolean, List<User>> group3 = StreamMain02.createUser().stream().collect(Collectors.partitioningBy(v -> v.getAge() > 10));
    }

    // peek操作一般用于不想改变流中元素本身的类型或者只想元素的内部状态时；
    // map则用于改变流中元素本身类型，即从元素中派生出另一种类型的操作。
    // 流不变，但会把每个元素传入fun执行，可以用作调试
    @Test
    public void peekUse() {
        List<User> user = StreamMain02.createUser();
        Stream<User> stream = user.stream();
        String s = stream.map(User::getName).findFirst().orElse("");
        System.out.println(s);
    }

    @Test
    public void test1() {
        List<User> user = StreamMain02.createUser();
        List<Object> all = new ArrayList<>();
        for (User user1 : user) {
            Map<String, Object> collect = user1.getMaps().stream()
                    .flatMap(m -> m.entrySet().stream())
                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> b));
            List<Object> collect1 = new ArrayList<>(collect.values());
            all.addAll(collect1);
        }
        System.out.println(all);
    }

    @Test
    public void countBase() {
        // 用户年龄求和，如果年龄有null，先filter过滤
        System.out.println(StreamMain02.createUser().stream().filter(e -> e.getAge() != null)
                .mapToInt(User::getAge).sum());
        System.out.println(StreamMain02.createUser().stream().filter(e -> e.getAge() != null)
                .mapToInt(User::getAge).average());
        // 3、最大最小年龄
        Integer maxAge = StreamMain02.createUser().stream().map(User::getAge)
                .collect(Collectors.maxBy(Integer::compare)).get(); // 20
        System.out.println(maxAge);
        System.out.println(StreamMain02.createUser().stream().map(User::getAge).min(Integer::compare));
    }


}
